package demoMod.anm2editor.utils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

class Pair<K, V> {
    private final K key;
    private final V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return this.key;
    }

    public V getValue() {
        return this.value;
    }

    public String toString() {
        return this.key + "=" + this.value;
    }

    public boolean equals(Object o) {
        if (!(o instanceof Pair)) {
            return false;
        } else {
            Pair<K, V> other = (Pair<K, V>) o;
            return Objects.equals(this.key, other.key) && Objects.equals(this.value, other.value);
        }
    }

    public int hashCode() {
        return Objects.hash(this.key, this.value);
    }
}

public class ReflectionHacks {
    private static final Map<Pair<Class<?>, String>, Field> fieldMap = new HashMap<>();
    private static final Map<String, Method> methodMap = new HashMap<>();

    private ReflectionHacks() {
    }

    private static String toDescriptor(Class<?> clz, String methodName, Class<?>... parameterTypes) {
        StringBuilder buf = new StringBuilder();
        buf.append(clz.getName().replace('.', '/')).append('.').append(methodName).append(":(");
        int var5 = parameterTypes.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            Class<?> paramType = ((Class[]) parameterTypes)[var6];
            toDescriptor(buf, paramType);
        }

        buf.append(')');
        return buf.toString();
    }

    private static void toDescriptor(StringBuilder buf, Class<?> clz) {
        if (clz.isPrimitive()) {
            if (clz == Byte.TYPE) {
                buf.append('B');
            } else if (clz == Character.TYPE) {
                buf.append('C');
            } else if (clz == Float.TYPE) {
                buf.append('F');
            } else if (clz == Double.TYPE) {
                buf.append('D');
            } else if (clz == Integer.TYPE) {
                buf.append('I');
            } else if (clz == Long.TYPE) {
                buf.append('J');
            } else if (clz == Short.TYPE) {
                buf.append('S');
            } else if (clz == Boolean.TYPE) {
                buf.append('Z');
            } else {
                if (clz != Void.TYPE) {
                    throw new RuntimeException("Unrecognized primitive " + clz);
                }

                buf.append('V');
            }
        } else if (clz.isArray()) {
            buf.append('[');
            toDescriptor(buf, clz.getComponentType());
        } else {
            buf.append('L').append(clz.getName().replace('.', '/')).append(';');
        }

    }

    public static Field getCachedField(Class<?> clz, String fieldName) {
        Pair<Class<?>, String> pair = new Pair<>(clz, fieldName);
        Field ret = fieldMap.get(pair);
        if (ret == null) {
            try {
                ret = clz.getDeclaredField(fieldName);
                ret.setAccessible(true);
                fieldMap.put(pair, ret);
            } catch (NoSuchFieldException var5) {
                var5.printStackTrace();
            }
        }

        return ret;
    }

    public static Method getCachedMethod(Class<?> clz, String methodName, Class<?>... parameterTypes) {
        String descriptor = toDescriptor(clz, methodName, parameterTypes);
        Method ret = methodMap.get(descriptor);
        if (ret == null) {
            try {
                ret = clz.getDeclaredMethod(methodName, parameterTypes);
                ret.setAccessible(true);
                methodMap.put(descriptor, ret);
            } catch (NoSuchMethodException var6) {
                var6.printStackTrace();
            }
        }

        return ret;
    }

    public static <T> T getPrivateStatic(Class<?> objClass, String fieldName) {
        try {
            return (T) getCachedField(objClass, fieldName).get(null);
        } catch (Exception var3) {
            return null;
        }
    }

    public static void setPrivateStatic(Class<?> objClass, String fieldName, Object newValue) {
        try {
            getCachedField(objClass, fieldName).set(null, newValue);
        } catch (Exception var4) {
            var4.printStackTrace();
        }

    }

    public static void setPrivateStaticFinal(Class<?> objClass, String fieldName, Object newValue) {
        try {
            Field f = getCachedField(objClass, fieldName);
            Field modifiers = getCachedField(Field.class, "modifiers");
            modifiers.setInt(f, f.getModifiers() & -17);
            f.set(null, newValue);
            modifiers.setInt(f, f.getModifiers() & 16);
        } catch (Exception var5) {
            System.out.println("Exception occurred when setting private static (final) field " + fieldName + " of " + objClass.getName());
            var5.printStackTrace();
        }

    }

    public static <T> T getPrivate(Object obj, Class<?> objClass, String fieldName) {
        try {
            return (T) getCachedField(objClass, fieldName).get(obj);
        } catch (Exception var4) {
            return null;
        }
    }

    public static void setPrivate(Object obj, Class<?> objClass, String fieldName, Object newValue) {
        try {
            getCachedField(objClass, fieldName).set(obj, newValue);
        } catch (Exception var5) {
            System.out.println("Exception occurred when setting private field " + fieldName + " of " + objClass.getName());
            var5.printStackTrace();
        }

    }

    public static void setPrivateFinal(Object obj, Class<?> objClass, String fieldName, Object newValue) {
        try {
            Field f = getCachedField(objClass, fieldName);
            Field modifiers = getCachedField(Field.class, "modifiers");
            modifiers.setInt(f, f.getModifiers() & -17);
            f.set(obj, newValue);
            modifiers.setInt(f, f.getModifiers() & 16);
        } catch (Exception var6) {
            System.out.println("Exception occurred when setting private (final) field " + fieldName + " of " + objClass.getName());
            var6.printStackTrace();
        }

    }

    public static <T> T getPrivateInherited(Object obj, Class<?> objClass, String fieldName) {
        objClass = objClass.getSuperclass();

        while(objClass != null && objClass != Object.class) {
            try {
                Field f = objClass.getDeclaredField(fieldName);
                f.setAccessible(true);

                try {
                    return (T) f.get(obj);
                } catch (IllegalAccessException var5) {
                    return null;
                }
            } catch (NoSuchFieldException var6) {
                objClass = objClass.getSuperclass();
            }
        }

        return null;
    }

    public static void setPrivateInherited(Object obj, Class<?> objClass, String fieldName, Object newValue) {
        objClass = objClass.getSuperclass();

        while(objClass != null && objClass != Object.class) {
            try {
                Field f = objClass.getDeclaredField(fieldName);
                f.setAccessible(true);

                try {
                    f.set(obj, newValue);
                } catch (IllegalAccessException var6) {
                    System.out.println("Exception occurred when setting private field " + fieldName + " of the superclass of " + objClass.getName());
                    var6.printStackTrace();
                }

                return;
            } catch (NoSuchFieldException var7) {
                objClass = objClass.getSuperclass();
            }
        }

    }

    public static ReflectionHacks.RMethod privateMethod(Class<?> objClass, String methodName, Class<?>... parameterTypes) {
        return new ReflectionHacks.RMethod(objClass, methodName, parameterTypes);
    }

    public static ReflectionHacks.RStaticMethod privateStaticMethod(Class<?> objClass, String methodName, Class<?>... parameterTypes) {
        return new ReflectionHacks.RStaticMethod(objClass, methodName, parameterTypes);
    }

    public static class RStaticMethod extends ReflectionHacks.RMethod {
        private RStaticMethod(Class<?> clz, String methodName, Class<?>... parameterTypes) {
            super(clz, methodName, parameterTypes);
        }

        public <R> R invoke(Object... args) {
            return super.invoke(null, args);
        }
    }

    public static class RMethod {
        private final Method method;

        private RMethod(Class<?> clz, String methodName, Class<?>... parameterTypes) {
            this.method = ReflectionHacks.getCachedMethod(clz, methodName, parameterTypes);
        }

        public <R> R invoke(Object instance, Object... args) {
            try {
                return (R) this.method.invoke(instance, args);
            } catch (Exception var4) {
                throw new RuntimeException(var4);
            }
        }
    }
}
